package owl2vowl.parser.vowl.property;

import java.util.Iterator;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDataPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLDataRange;
import org.semanticweb.owlapi.model.OWLEquivalentDataPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLFunctionalDataPropertyAxiom;
import org.semanticweb.owlapi.model.OWLProperty;
import org.semanticweb.owlapi.model.OWLSubDataPropertyOfAxiom;

import owl2vowl.constants.VowlAttribute;
import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.nodes.AbstractNode;
import owl2vowl.model.entities.nodes.classes.NullClass;
import owl2vowl.model.entities.nodes.datatypes.DatatypeReference;
import owl2vowl.model.entities.properties.AbstractProperty;
import owl2vowl.model.entities.properties.VowlDatatypeProperty;

public class DataPropertyVisitor extends PropertyVisitor {
	private Logger logger = LogManager.getLogger(DataPropertyVisitor.class);

	public DataPropertyVisitor(VowlData vowlData, OWLProperty owlObjectProperty) {
		super(vowlData, owlObjectProperty);
	}

	public void visit(OWLFunctionalDataPropertyAxiom axiom) {
		VowlDatatypeProperty prop = this.vowlData.getDatatypePropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.FUNCTIONAL);
	}

	public void visit(OWLDataPropertyRangeAxiom axiom) {
		VowlDatatypeProperty prop = this.vowlData.getDatatypePropertyForIri(this.owlObjectProperty.getIRI());
		if (!((OWLDataRange) axiom.getRange()).isOWLDatatype()) {
			this.logger.info("DataPropertyRange is no datatype: " + axiom);
		} else {
			AbstractNode node = (AbstractNode) ((OWLDataRange) axiom.getRange())
					.accept(new DomainRangeVisitor(this.owlObjectProperty, this.vowlData));
			if (node instanceof NullClass) {
				this.logger.info("Skipped anonymous data range: " + axiom.getRange());
			} else {
				DatatypeReference reference = (DatatypeReference) node;
				prop.addRange(reference.getIri());
				reference.addInGoingProperty(prop.getIri());
			}
		}
	}

	public void visit(OWLDataPropertyDomainAxiom axiom) {
		VowlDatatypeProperty prop = this.vowlData.getDatatypePropertyForIri(this.owlObjectProperty.getIRI());
		IRI domainIri;
		if (((OWLClassExpression) axiom.getDomain()).isAnonymous()) {
			AbstractNode anonymClass = (AbstractNode) ((OWLClassExpression) axiom.getDomain())
					.accept(new DomainRangeVisitor(this.owlObjectProperty, this.vowlData));
			if (anonymClass instanceof NullClass) {
				this.logger.info("Skipped anonymous object domain: " + axiom.getDomain());
				return;
			}

			domainIri = anonymClass.getIri();
		} else {
			if (((OWLClassExpression) axiom.getDomain()).asOWLClass().isOWLThing()) {
				return;
			}

			domainIri = ((OWLClassExpression) axiom.getDomain()).asOWLClass().getIRI();
		}

		prop.addDomain(domainIri);
		this.vowlData.getClassForIri(domainIri).addOutGoingProperty(prop.getIri());
	}

	public void visit(OWLSubDataPropertyOfAxiom axiom) {
		this.logger.info("Sub Data property axiom not supported yet.");
	}

	public void visit(OWLEquivalentDataPropertiesAxiom axiom) {
		AbstractProperty base = this.vowlData.getPropertyForIri(this.owlObjectProperty.getIRI());
		Iterator arg2 = axiom.getProperties().iterator();

		while (arg2.hasNext()) {
			OWLDataPropertyExpression expr = (OWLDataPropertyExpression) arg2.next();
			if (expr.isAnonymous()) {
				this.logger.info("Anonysmous equivalent prop: " + expr);
			} else if (!expr.asOWLDataProperty().getIRI().equals(base.getIri())) {
				base.addEquivalentElement(expr.asOWLDataProperty().getIRI());
			}
		}

	}
}